#include "Timer.h"

Timer::Timer()
    : mLastStartTime(0)
    , mInPauseTime(0)
    , mPreviousTickTime(0)
    , mCurrentTickTime(0)
    , mSecondsPerCount(0.0)
    , mDeltaTime(-1.0)
    , mIsStopped(false)
{
    // Get the frequency of the high-resolution performance counter, if one exists. 
    // The frequency cannot change while the system is running.
    uint64_t countsPerSec;
    QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER *> (&countsPerSec));
    mSecondsPerCount = 1.0 / static_cast<double> (countsPerSec);
}

float Timer::activeTime() const {
    // If we are stopped, do not count the time that has passed since we stopped.
    //
    // ----*---------------*------------------------------*------> time
    // mLastStartTime  mLastStopTime                mCurrentTickTime
    //
    // ELSE:
    // The distance mCurrentTickTime - mLastStartTime includes paused time,
    // which we do not want to count. To correct this, we can subtract 
    // the paused time from mCurrentTickTime:  
    //
    //  (mCurrentTickTime - mInPauseTime) - mLastStartTime 
    //
    //                     |<---mInPauseTime-->|
    // ----*---------------*-------------------*-------------*------> time
    //  mLastStartTime  mLastStopTime       newStartTime  mCurrentTickTime
    const float time = (mIsStopped) 
                       ? static_cast<float> ((mLastStopTime - mLastStartTime) * mSecondsPerCount)
                       : static_cast<float> ((mCurrentTickTime - mInPauseTime - mLastStartTime) * mSecondsPerCount);
    
    return time;
}

void Timer::reset() {
    // Last start time will be the current elapsed time.
    QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *> (&mLastStartTime));
    
    // Reset the previous tick frame.
    mPreviousTickTime = mLastStartTime;
    
    // As we are resetting, then there is no last stop time.
    mLastStopTime = 0;
    mIsStopped  = false;
}

void Timer::start() {
    // Accumulate the time elapsed between stop and start pairs.
    //
    //                     |<-------d------->|
    // ----*---------------*-----------------*------------> time
    //  mLastStartTime    mLastStopTime      newStartTime     
    
    if (mIsStopped) {
        // If timer was stopped then the previous tick time will be the new start time.
        QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *> (&mPreviousTickTime));
        
        // Update the total in pause time.
        mInPauseTime += (mPreviousTickTime - mLastStopTime);	
        
        // Because the timer was stopped and now started, then there is no last stop time.
        mLastStopTime = 0;
        mIsStopped = false;
    }
}

void Timer::stop() {
    if (!mIsStopped) {
        // If the timer is not stopped, then we need to update the 
        // last stop time.
        QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *> (&mLastStopTime));
        
        mIsStopped = true;
    }
}

void Timer::tick() {
    // If timer is stopped, then there is no delta time between
    // previous and current tick time.
    if (mIsStopped) {
        mDeltaTime = 0.0;
    } else {
        // Update current tick time.
        QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER *> (&mCurrentTickTime));
        
        // Time difference between this frame and the previous.
        // Force nonnegative. The DXSDK's CDXUTTimer mentions that if the 
        // processor goes into a power save mode or we get shuffled to another
        // processor, then mDeltaTime can be negative.
        const double newDeltaTime = (mCurrentTickTime - mPreviousTickTime) * mSecondsPerCount;
                                    mDeltaTime = (newDeltaTime < 0.0) ? 0.0 : newDeltaTime;

        // Prepare for next frame.
        mPreviousTickTime = mCurrentTickTime;
    }
}

double Timer::deltaTime() const {
    return mDeltaTime;
}